#include "Delay.h"
#include "FreeRTOS.h"
#include "task.h"

#ifdef FREERTOS_CONFIG_H

/**
 ******************************************************************************
 * @file    Delay.c
 * @brief   FreeRTOS与裸机下延时函数实现
 * @note    本文件包含基于SysTick的微秒、毫秒、秒级延时函数，
 *          兼容FreeRTOS和裸机两种环境。
 *          - FreeRTOS下仅读取SysTick，不影响系统节拍。
 *          - 裸机下可直接配置SysTick实现精确延时。
 * @author  18408107475@163.com
 * @date    2025-07-22
 ******************************************************************************
 */

/**
 ******************************************************************************
 * @brief   微秒级延时（FreeRTOS环境下）
 * @param   xus 微秒数（uint32_t）
 * @retval  无
 * @details 
 *   - 通过读取SysTick当前值，计算经过的tick数实现延时。
 *   - 进入临界区读取SysTick，防止任务切换导致数据不一致。
 *   - 不会影响FreeRTOS调度器。
 *   - 适合任务内短延时，阻塞当前任务。
 * @note    SystemCoreClock需正确配置为系统主频。
 ******************************************************************************
 */
void Delay_us(uint32_t xus)
{
    uint32_t ticks_per_us   = SystemCoreClock / 1000000; // 1us对应的SysTick计数
    uint32_t total_ticks    = xus * ticks_per_us;        // 目标总tick数
    uint32_t elapsed_ticks  = 0;                         // 已经过的tick数

    taskENTER_CRITICAL();                                // 进入临界区，防止任务切换
    uint32_t start_ticks   = SysTick->VAL;               // 记录起始SysTick值
    taskEXIT_CRITICAL();                                 // 退出临界区

    while(elapsed_ticks < total_ticks){                  // 未达到目标tick数则循环
        taskENTER_CRITICAL();                            // 进入临界区
        uint32_t curr_ticks = SysTick->VAL;              // 读取当前SysTick值
        taskEXIT_CRITICAL();                             // 退出临界区
        
        uint32_t delta;                                  // 本次经过的tick数
        // SysTick为递减计数器，需判断是否溢出
        delta = (curr_ticks <= start_ticks) ? 
                (start_ticks - curr_ticks)  : 
                ((SysTick->LOAD - curr_ticks) + start_ticks);

        elapsed_ticks  += delta;                         // 累加已过tick
        start_ticks    = curr_ticks;                     // 更新起始点

        // 检查SysTick是否溢出（COUNTFLAG置位）
        if(SysTick->CTRL & 0x00010000){
            elapsed_ticks += (SysTick->LOAD + 1);        // 补偿溢出tick
        }
    }
}

/**
 ******************************************************************************
 * @brief   毫秒级延时（FreeRTOS环境下）
 * @param   xms 毫秒数（uint32_t）
 * @retval  无
 * @details 
 *   - 逻辑同Delay_us，单位为ms。
 *   - 适合任务内短延时，阻塞当前任务。
 ******************************************************************************
 */
void Delay_ms(uint32_t xms)
{
    uint32_t ticks_per_ms   = SystemCoreClock / 1000;    // 1ms对应的SysTick计数
    uint32_t total_ticks    = xms * ticks_per_ms;        // 目标总tick数
    uint32_t elapsed_ticks  = 0;                         // 已经过的tick数

    taskENTER_CRITICAL();                                // 进入临界区
    uint32_t start_ticks   = SysTick->VAL;               // 记录起始SysTick值
    taskEXIT_CRITICAL();                                 // 退出临界区

    while(elapsed_ticks < total_ticks){                  // 未达到目标tick数则循环
        taskENTER_CRITICAL();                            // 进入临界区
        uint32_t curr_ticks = SysTick->VAL;              // 读取当前SysTick值
        taskEXIT_CRITICAL();                             // 退出临界区
        
        uint32_t delta;                                  // 本次经过的tick数
        delta = (curr_ticks <= start_ticks) ? 
                (start_ticks - curr_ticks)  : 
                ((SysTick->LOAD - curr_ticks) + start_ticks);

        elapsed_ticks  += delta;                         // 累加已过tick
        start_ticks    = curr_ticks;                     // 更新起始点

        if(SysTick->CTRL & 0x00010000){
            elapsed_ticks += (SysTick->LOAD + 1);        // 补偿溢出tick
        }
    }
}

/**
 ******************************************************************************
 * @brief   秒级延时（FreeRTOS环境下）
 * @param   xs 秒数（uint32_t）
 * @retval  无
 * @details 
 *   - 逻辑同Delay_us，单位为秒。
 *   - 适合任务内短延时，阻塞当前任务。
 ******************************************************************************
 */
void Delay_s(uint32_t xs)
{
    uint32_t ticks_per_s    = SystemCoreClock;           // 1s对应的SysTick计数
    uint32_t total_ticks    = xs * ticks_per_s;          // 目标总tick数
    uint32_t elapsed_ticks  = 0;                         // 已经过的tick数

    taskENTER_CRITICAL();                                // 进入临界区
    uint32_t start_ticks   = SysTick->VAL;               // 记录起始SysTick值
    taskEXIT_CRITICAL();                                 // 退出临界区

    while(elapsed_ticks < total_ticks){                  // 未达到目标tick数则循环
        taskENTER_CRITICAL();                            // 进入临界区
        uint32_t curr_ticks = SysTick->VAL;              // 读取当前SysTick值
        taskEXIT_CRITICAL();                             // 退出临界区
        
        uint32_t delta;                                  // 本次经过的tick数
        delta = (curr_ticks <= start_ticks) ? 
                (start_ticks - curr_ticks)  : 
                ((SysTick->LOAD - curr_ticks) + start_ticks);

        elapsed_ticks  += delta;                         // 累加已过tick
        start_ticks    = curr_ticks;                     // 更新起始点

        if(SysTick->CTRL & 0x00010000){
            elapsed_ticks += (SysTick->LOAD + 1);        // 补偿溢出tick
        }
    }
}

#else

/**
 ******************************************************************************
 * @brief   微秒级精确延时（裸机环境下，基于SysTick）
 * @param   xus 微秒数（0~1864134 us）
 * @retval  无
 * @details 
 *   - 直接配置SysTick，阻塞式等待COUNTFLAG。
 *   - 适合裸机短延时，精度高。
 *   - 9MHz时钟下最大延时约1864134us。
 * @note    不适用于中断服务函数，修改主频需同步调整。
 ******************************************************************************
 */
void Delay_us(uint32_t xus)
{
    SysTick->CTRL = 0;                      // 关闭SysTick（清空控制寄存器）
    SysTick->LOAD = (9 * xus) - 1;          // 设置重装载值（9MHz时钟）
    SysTick->VAL  = 0x00;                   // 清空计数器
    SysTick->CTRL = 0x00000005;             // 启用SysTick（内核时钟）
    while(!(SysTick->CTRL & 0x00010000));   // 等待计数完成（检查COUNTFLAG）
    SysTick->CTRL = 0;                      // 关闭SysTick
}

/**
 ******************************************************************************
 * @brief   毫秒级延时（裸机环境下，基于Delay_us）
 * @param   xms 毫秒数（0~1864 ms）
 * @retval  无
 * @details 
 *   - 通过设置SysTick重装载值实现ms延时。
 *   - 存在函数调用开销，精度略低于Delay_us。
 *   - 最大延时受Delay_us限制。
 ******************************************************************************
 */
void Delay_ms(uint32_t xms)
{
    SysTick->CTRL = 0;                      // 关闭SysTick（清空控制寄存器）
    SysTick->LOAD = (9*1000 * xms) - 1;     // 设置重装载值（9MHz时钟）
    SysTick->VAL  = 0x00;                   // 清空计数器
    SysTick->CTRL = 0x00000001;             // 启用SysTick（使用内核时钟）
    while(!(SysTick->CTRL & 0x00010000));   // 等待计数完成（检查COUNTFLAG）
    SysTick->CTRL = 0;  
}

/**
 ******************************************************************************
 * @brief   秒级延时（裸机环境下，基于Delay_ms）
 * @param   xs 秒数（0~1 s）
 * @retval  无
 * @details 
 *   - 通过设置SysTick重装载值实现秒级延时。
 *   - 适用于非精确长延时场景。
 *   - 长时间阻塞会影响系统实时性。
 ******************************************************************************
 */
void Delay_s(uint32_t xs)
{
    SysTick->CTRL = 0;                      // 关闭SysTick（清空控制寄存器）
    SysTick->LOAD = (9*1000000 * xs) - 1;   // 设置重装载值（9MHz时钟）
    SysTick->VAL  = 0x00;                   // 清空计数器
    SysTick->CTRL = 0x00000001;             // 启用SysTick（使用内核时钟）
    while(!(SysTick->CTRL & 0x00010000));   // 等待计数完成（检查COUNTFLAG）
    SysTick->CTRL = 0;
}

#endif // DEBUG FREERTOS_CONFIG_H
